package org.yunai.yfserver.async;

import org.yunai.yfserver.server.IMessageProcessor;

import java.util.concurrent.ExecutorService;

/**
 * 异步执行Io操作策略
 * User: yunai
 * Date: 13-3-28
 * Time: 下午1:47
 */
class AsyncIoOperation {

    /**
     * IO具体操作
     */
    private final IIoOperation operation;
    /**
     * 操作当前状态
     */
    private volatile IIoOperation.State state;
    /**
     * 线程池
     */
    private final ExecutorService executor;
    /**
     * 消息队列<br />
     * 当{@link org.yunai.yfserver.async.IIoOperation#doIo()}后state处于{@link IIoOperation.State#IO_DONE}时添加消息
     */
    private final IMessageProcessor msgProcessor;

    public AsyncIoOperation(IIoOperation operation, ExecutorService executor, IMessageProcessor msgProcessor) {
        this.operation = operation;
        this.executor = executor;
        this.msgProcessor = msgProcessor;
        this.state = IIoOperation.State.INITIALIZED;
    }

    /**
     * 异步执行IO操作<br />
     * 1. INITIALIZED: 进行操作doStart()。<br />
     * >>若操作状态（STARTED）， 则异步执行execute()。<br />
     * >>若操作状态（IO_DONE），则同步执行doFinish()。<br />
     * 2. STARTED: 进行doIo操作。<br />
     * >>若操作状态(IO_DONE)，则给msgProcessor里添加完成Io操作的消息。<br />
     * 3. IO_DONE: 进行doFinish()操作<br />
     * <b>若调用doStart()的线程为单线程，请保证doStart()执行效率足够快</b><br />
     * <b>若调用doFinish()的线程为单线程，请保证doFinish()执行效率足够快</b><br />
     */
    public void execute() {
        switch (state) {
            case INITIALIZED:
                state = operation.doStart();
                if (state == IIoOperation.State.STARTED) {
                    executor.execute(new Runnable() {
                        @Override
                        public void run() {
                            execute();
                        }
                    });
                } else if (state == IIoOperation.State.IO_DONE) {
                    state = operation.doFinish();
                }
                break;
            case STARTED:
                state = operation.doIo();
                if (state == IIoOperation.State.IO_DONE) {
                    msgProcessor.put(new ScheduleAsyncFinishMessage(System.currentTimeMillis(), this));
                }
                break;
            case IO_DONE:
                state = operation.doFinish();
                break;
        }
    }

}
